package com.gzhryc.jdbc.models;

import com.gzhryc.jdbc.enums.DbType;
import org.apache.commons.lang3.StringUtils;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Serializable;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 数据库连接信息
 * @author 张浩
 * @version 1.0.1
 */
public class ConnectInfo implements Serializable{
	
	private static final long serialVersionUID = 1L;

	public final static String MYSQL_DRIVER_CLASS = "com.mysql.jdbc.Driver";
	
	public final static String ORACLE_DRIVER_CLASS = "oracle.jdbc.driver.OracleDriver";
	
	public final static String H2_DRIVER_CLASS = "org.h2.Driver";
	
	private String key;
	private String driverClassName;
	private DbType dbType;
	private String url;
	private String ip;
	private Integer port;
	private String dbName;
	private String userName;
	private String password;
	private Integer maxActive = 30;					//最大连接数量 
	private Integer maxIdle = 10;					//最大空闲连接
	private Integer maxWait = 3000;					//超时等待时间(以毫秒为单位)
	private Boolean removeAbandoned = true;			//否自动回收超时连接 
	private Integer removeAbandonedTimeout = 60;	//超时时间(以秒数为单位) 
	private Boolean testOnBorrow = false;
	private Boolean logAbandoned = false;			//是否在自动回收超时连接的时候打印连接的超时错误

	private Integer logLevel = 1;		//1:不显示SQL日志，2:显示SQL日志

	public ConnectInfo(DbType dbType) {
		this.dbType = dbType;
	}

	public ConnectInfo(Properties prop) throws NullPointerException {
		if (prop.containsKey("dbType")) {
			this.dbType = DbType.valueOf(prop.getProperty("dbType"));
			prop.remove("dbType");
		} else if (prop.containsKey("driverClassName")) {
			this.dbType = ConnectInfo.getDbTypeByDriverClass(prop
					.getProperty("driverClassName"));
		} else {
			throw new NullPointerException("dbType is null");
		}
		if (prop.containsKey("ip")) {
			this.ip = prop.getProperty("ip");
		}
		if (prop.containsKey("port") && StringUtils.isNotBlank(prop.getProperty("port"))) {
			this.port = Integer.valueOf(prop.getProperty("port"));
		}
		if (prop.containsKey("dbName")) {
			this.dbName = prop.getProperty("dbName");
		}
		if(prop.containsKey("url")){
			this.url = prop.getProperty("url");
		}
		if(prop.containsKey("username")){
			this.userName = prop.getProperty("username");
		}
		if(prop.containsKey("password")){
			this.password = prop.getProperty("password");
		}
		if(prop.containsKey("maxActive") && StringUtils.isNotBlank(prop.getProperty("maxActive"))){
			this.maxActive = Integer.valueOf(prop.getProperty("maxActive"));
		}
		if(prop.containsKey("maxIdle") && StringUtils.isNotBlank(prop.getProperty("maxIdle"))){
			this.maxIdle = Integer.valueOf(prop.getProperty("maxIdle"));
		}
		if(prop.containsKey("maxWait") && StringUtils.isNotBlank(prop.getProperty("maxWait"))){
			this.maxWait = Integer.valueOf(prop.getProperty("maxWait"));
		}
		if(prop.containsKey("removeAbandoned") && StringUtils.isNotBlank(prop.getProperty("removeAbandoned"))){
			this.removeAbandoned = Boolean.valueOf(prop.getProperty("removeAbandoned"));
		}
		if(prop.containsKey("removeAbandonedTimeout") && StringUtils.isNotBlank(prop.getProperty("removeAbandonedTimeout"))){
			this.removeAbandonedTimeout = Integer.valueOf(prop.getProperty("removeAbandonedTimeout"));
		}
		if(prop.containsKey("testOnBorrow") && StringUtils.isNotBlank(prop.getProperty("testOnBorrow"))){
			this.testOnBorrow = Boolean.valueOf(prop.getProperty("testOnBorrow"));
		}
		if(prop.containsKey("logAbandoned") && StringUtils.isNotBlank(prop.getProperty("logAbandoned"))){
			this.logAbandoned = Boolean.valueOf(prop.getProperty("logAbandoned"));
		}
		if(prop.containsKey("logLevel") && StringUtils.isNotBlank(prop.getProperty("logLevel"))){
			this.logLevel = Integer.valueOf(prop.getProperty("logLevel"));
		}
	}

	public ConnectInfo(DbType dbType, String ip, Integer port, String dbName,
			String userName, String password) {
		this.dbType = dbType;
		this.ip = ip;
		this.port = port;
		this.dbName = dbName;
		this.userName = userName;
		this.password = password;
	}

	public String getDriverClassName() {
		if (StringUtils.isBlank(this.driverClassName) && this.dbType != null) {
			if (DbType.mysql.equals(dbType)) {
				this.setDriverClassName(MYSQL_DRIVER_CLASS);
			} else if (DbType.oracle.equals(dbType)) {
				this.setDriverClassName(ORACLE_DRIVER_CLASS);
			} else if (DbType.h2.equals(dbType)) {
				this.setDriverClassName(H2_DRIVER_CLASS);
			}
		}
		return driverClassName;
	}

	public void setDriverClassName(String driverClassName) {
		this.driverClassName = driverClassName;
	}

	public String getUrl() {
		if (StringUtils.isBlank(this.url) && this.dbType != null
				&& StringUtils.isNotBlank(this.ip) && port != null
				&& StringUtils.isNotBlank(this.dbName)) {
			if (DbType.mysql.equals(dbType)) {
				this.setUrl("jdbc:mysql://" + ip + ":" + port + "/" + dbName);
			} else if (DbType.oracle.equals(dbType)) {
				this.setUrl("jdbc:oracle:thin:@" + ip + ":" + port + ":" + dbName);
			} else if (DbType.h2.equals(dbType)) {
				this.setUrl("jdbc:h2:tcp://" + ip + ":" + port + "/" + dbName);
			}
		}
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public String getPassword() {
		if(password == null){
			return "";
		}
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public Integer getMaxActive() {
		return maxActive;
	}

	public void setMaxActive(Integer maxActive) {
		this.maxActive = maxActive;
	}

	public Integer getMaxIdle() {
		return maxIdle;
	}

	public void setMaxIdle(Integer maxIdle) {
		this.maxIdle = maxIdle;
	}

	public Integer getMaxWait() {
		return maxWait;
	}

	public void setMaxWait(Integer maxWait) {
		this.maxWait = maxWait;
	}

	public Boolean getRemoveAbandoned() {
		return removeAbandoned;
	}

	public void setRemoveAbandoned(Boolean removeAbandoned) {
		this.removeAbandoned = removeAbandoned;
	}

	public Integer getRemoveAbandonedTimeout() {
		return removeAbandonedTimeout;
	}

	public void setRemoveAbandonedTimeout(Integer removeAbandonedTimeout) {
		this.removeAbandonedTimeout = removeAbandonedTimeout;
	}

	public Boolean getTestOnBorrow() {
		return testOnBorrow;
	}

	public void setTestOnBorrow(Boolean testOnBorrow) {
		this.testOnBorrow = testOnBorrow;
	}

	public Boolean getLogAbandoned() {
		return logAbandoned;
	}

	public void setLogAbandoned(Boolean logAbandoned) {
		this.logAbandoned = logAbandoned;
	}

	public Integer getLogLevel() {
		return logLevel;
	}

	public void setLogLevel(Integer logLevel) {
		this.logLevel = logLevel;
	}

	public String getKey() {
		return key;
	}

	public void setKey(String key) {
		this.key = key;
	}

	public String getIp() {
		if(StringUtils.isBlank(this.ip) && StringUtils.isNotBlank(this.url)){
			if (DbType.mysql.equals(dbType)) {
				Pattern pat = Pattern.compile("^jdbc:mysql://([^:]+):.*$");
				Matcher mat = pat.matcher(this.url);
				if(mat.find()){
					this.ip = mat.group(1);
				}
			}else if (DbType.h2.equals(dbType)) {
				//jdbc:h2:tcp://127.0.0.1:19093/~/cat-database-1
				Pattern pat = Pattern.compile("^jdbc:h2:tcp://([^:]+):.*$");
				Matcher mat = pat.matcher(this.url);
				if(mat.find()){
					this.ip = mat.group(1);
				}
			}
		}
		return ip;
	}

	public void setIp(String ip) {
		this.ip = ip;
	}

	public Integer getPort() {
		if(port == null && StringUtils.isNotBlank(this.url)){
			if (DbType.mysql.equals(dbType)) {
				Pattern pat = Pattern.compile("^jdbc:mysql://[^:]+:([^/]+)/.*$");
				Matcher mat = pat.matcher(this.url);
				if(mat.find()){
					port = Integer.valueOf(mat.group(1));
				}
			}else if (DbType.h2.equals(dbType)) {
				//jdbc:h2:tcp://127.0.0.1:19093/~/cat-database-1
				Pattern pat = Pattern.compile("^jdbc:h2:tcp://[^:]+:([^/]+)/.*$");
				Matcher mat = pat.matcher(this.url);
				if(mat.find()){
					port = Integer.valueOf(mat.group(1));
				}
			}
		}
		return port;
	}

	public void setPort(Integer port) {
		this.port = port;
	}

	public String getDbName() {
		if(dbName == null && StringUtils.isNotBlank(this.url)){
			if (DbType.mysql.equals(dbType)) {
				Pattern pat = Pattern.compile("^jdbc:mysql://[^:]+:[^/]+/([^?]+).*$");
				Matcher mat = pat.matcher(this.url);
				if(mat.find()){
					dbName = mat.group(1);
				}
			}else if (DbType.h2.equals(dbType)) {
				//jdbc:h2:tcp://127.0.0.1:19093/~/cat-database-1
				Pattern pat = Pattern.compile("^jdbc:h2:tcp://[^:]+:[^/]+/~/(.*)$");
				Matcher mat = pat.matcher(this.url);
				if(mat.find()){
					dbName = mat.group(1);
				}
			}
		}
		return dbName;
	}

	public void setDbName(String dbName) {
		this.dbName = dbName;
	}

	public DbType getDbType() {
		return dbType;
	}

	public void setDbType(DbType dbType) {
		this.dbType = dbType;
	}

	public ConnectInfo clone(){
		ConnectInfo info = new ConnectInfo(this.getDbType(),this.getIp(),this.getPort(),
				this.getDbName(),this.getUserName(),this.getPassword());
		info.setDriverClassName(this.getDriverClassName());
		info.setLogAbandoned(this.getLogAbandoned());
		info.setMaxActive(this.getMaxActive());
		info.setMaxIdle(this.getMaxIdle());
		info.setMaxWait(this.getMaxWait());
		info.setRemoveAbandoned(this.getRemoveAbandoned());
		info.setRemoveAbandonedTimeout(this.getRemoveAbandonedTimeout());
		info.setTestOnBorrow(this.getTestOnBorrow());
		info.setUrl(this.url);
		info.setLogLevel(this.logLevel);
		return info;
	}
	
	public Properties getProperties() {
		Properties prop = new Properties();
		prop.setProperty("driverClassName", this.getDriverClassName());
		prop.setProperty("url", this.getUrl());
		prop.setProperty("username", userName);
		prop.setProperty("password", getPassword());
		prop.setProperty("maxActive", maxActive.toString());
		prop.setProperty("maxIdle", maxIdle.toString());
		prop.setProperty("maxWait", maxWait.toString());
		prop.setProperty("removeAbandoned", removeAbandoned.toString());
		prop.setProperty("removeAbandonedTimeout",removeAbandonedTimeout.toString());
		prop.setProperty("testOnBorrow", testOnBorrow.toString());
		prop.setProperty("logAbandoned", logAbandoned.toString());
		prop.setProperty("logLevel", logLevel.toString());
		return prop;
	}

	public static DbType getDbTypeByDriverClass(String driverClass) {
		if (MYSQL_DRIVER_CLASS.equals(driverClass)) {
			return DbType.mysql;
		} else if (ORACLE_DRIVER_CLASS.equals(driverClass)) {
			return DbType.oracle;
		} else if (H2_DRIVER_CLASS.equals(driverClass)) {
			return DbType.h2;
		}
		return null;
	}

	/**
	 * 根据配置文件创建数据库连接信息
	 * @param path
	 * @return
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	public static ConnectInfo create(String path) throws FileNotFoundException, IOException{
		Properties properties = new Properties();
		properties.load(new FileInputStream(path));
		return new ConnectInfo(properties);
	}

}
